﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
using eeenno.ReaderEngine.Utilities;

namespace eeenno.ReaderEngine.Adapters
{
    /// <summary>
    /// Processes Atom RSS feeds from Google Reader
    /// </summary>
    public class AtomEngine
    {
        private const int _NUMBER_OF_ATTRIBUTES = 10;
        private const string _NODE_KEYWORD = "entry";

        private const string _CONT_KEYWORD = "continuation";
        private const string _ID_KEYWORD = "id";
        private const string _DATE_KEYWORD = "gr:crawl-timestamp-msec";
        private const string _TITLE_KEYWORD = "title";
        private const string _TAG_KEYWORD = "category";
        private const string _SUMMARY_KEYWORD = "summary";
        private const string _CONTENT_KEYWORD = "content";
        private const string _LINK_KEYWORD = "link";
        private const string _AUTHOR_KEYWORD = "author";
        private const string _SOURCE_KEYWORD = "source";

        private const string _LABEL_KEYWORD = "label";
        private const string _URL_KEYWORD = "href";

        /// <summary>
        /// Parses feed data and extracts requested data
        /// Returned data in form of:
        /// 0 - title
        /// 1 - date
        /// 2 - id
        /// 3 - tag
        /// 4 - url
        /// 5 - summary
        /// 6 - author
        /// 7 - site link
        /// 8 - site name
        /// </summary> 
        /// <param name="feed_data">XML of the atom feed retured from Google Reader</param>
        /// <returns>data structure containing necessary data</returns>
        public static string[,] ProcessFeedData(string feed_data)
        {
            string[,] ret_val = new string[0,0];
            if (feed_data.Length > 0)
            {
                string cont_keyword = String.Empty;
                int offset = 0;
                XmlDocument feed = new XmlDocument();
                try
                {
                    using (XmlTextReader reader = new XmlTextReader(new StringReader(feed_data)))
                    {
                        feed.Load(reader);
                    }
                }
                catch (Exception e)
                {
                    return ret_val;
                }
                XmlNodeList article_nodes = feed.ChildNodes[1].ChildNodes;

                //iterate through and count the number of actual article nodes
                for (int i = 0; i < article_nodes.Count; i++)
                {
                    if (article_nodes[i].LocalName == _CONT_KEYWORD)
                    {
                        cont_keyword = article_nodes[i].InnerText;
                    }
                    if (article_nodes[i].LocalName == _NODE_KEYWORD)
                    {
                        offset = i;
                        ret_val = GetArray(article_nodes.Count - i, _NUMBER_OF_ATTRIBUTES);
                        break;
                    }
                }
                int array_length = ret_val.Length / _NUMBER_OF_ATTRIBUTES;
                //iterate through each node (article) in the feed
                for (int i = 0; i < array_length; i++)
                {
                    if (article_nodes[i + offset].LocalName.Equals(_NODE_KEYWORD))
                    {
                        XmlNode current_node = article_nodes[i + offset];
                        //retrieve the date (Google crawl date)
                        ret_val[i, 1] = ParseAttribute(current_node, _DATE_KEYWORD, false);
                        for (int j = 0; j < current_node.ChildNodes.Count; j++)
                        {
                            ret_val[i, 9] = cont_keyword;
                            switch (current_node.ChildNodes[j].LocalName)
                            {
                                case _TITLE_KEYWORD:
                                    ret_val[i, 0] = current_node.ChildNodes[j].InnerText;
                                    break;
                                case _ID_KEYWORD:
                                    ret_val[i, 2] = current_node.ChildNodes[j].InnerText;
                                    break;
                                case _TAG_KEYWORD:
                                    ret_val[i, 3] += ParseAttribute(current_node.ChildNodes[j], _LABEL_KEYWORD, true);
                                    break;
                                case _LINK_KEYWORD:
                                    ret_val[i, 4] = ParseAttribute(current_node.ChildNodes[j], _URL_KEYWORD, false);
                                    break;
                                case _SUMMARY_KEYWORD:
                                    ret_val[i, 5] = current_node.ChildNodes[j].InnerText;
                                    break;
                                case _CONTENT_KEYWORD:
                                    ret_val[i, 5] = current_node.ChildNodes[j].InnerText;
                                    break;
                                case _AUTHOR_KEYWORD:
                                    ret_val[i, 6] = current_node.ChildNodes[j].InnerText;
                                    break;
                                case _SOURCE_KEYWORD:
                                    ret_val[i, 7] = ParseSource(current_node.ChildNodes[j], _LINK_KEYWORD);
                                    ret_val[i, 8] = ParseSource(current_node.ChildNodes[j], _TITLE_KEYWORD);
                                    break;
                                default:
                                    break;
                            }
                        }
                    }
                }
            }
            return ret_val;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="node"></param>
        /// <param name="tag"></param>
        /// <returns></returns>
        private static string ParseSource(XmlNode node, string tag)
        {
            string ret_val = String.Empty;

            for (int i = 0; i < node.ChildNodes.Count; i++)
            {
                if (node.ChildNodes[i].LocalName.Equals(tag))
                {
                    switch (tag)
                    {
                        case _TITLE_KEYWORD:
                            ret_val = node.ChildNodes[i].InnerText;
                            break;
                        case _LINK_KEYWORD:
                            ret_val = ParseAttribute(node.ChildNodes[i], _URL_KEYWORD, false);
                            break;
                        default:
                            break;
                    }                     
                }
            }

            return ret_val;
        }

        /// <summary>
        /// Parses attributes out of XML
        /// </summary>
        /// <param name="node"></param>
        /// <param name="attribute"></param>
        /// <param name="is_tag"></param>
        /// <returns></returns>
        private static string ParseAttribute(XmlNode node, string attribute, bool is_tag)
        {
            string ret_val = String.Empty;

            XmlAttributeCollection attributes = node.Attributes;
            if(attributes[attribute] != null)
            {
                if (is_tag)
                {
                    ret_val += "~";
                }
                ret_val += attributes[attribute].Value;
            }

            return ret_val;
        }

        /// <summary>
        /// Returns an initialized array of requested size
        /// </summary>
        /// <param name="x_dim"></param>
        /// <param name="y_dim"></param>
        /// <returns></returns>
        private static string[,] GetArray(int x_dim, int y_dim)
        {
            string[,] ret_val = new string[x_dim, y_dim];
            for (int i = 0; i < x_dim; i++)
            {
                for (int j = 0; j < y_dim; j++)
                {
                    ret_val[i, j] = String.Empty;
                }
            }

            return ret_val;
        }        
    }
}
